// Revision History
// 2.6G:    No head & checksum
// 2.7 :    Transfers big data[filesize:4byte]
//          No bioscom(), File writing, Fix chcecksum bug
// 2.71:    if comport==0, the serial port is not oused.
// 2.8 :    /d:n n=12(9600),6(19200),3(38400),2(57600),1(115200)
//          /n:No init (default when /f)
// 2.81:    Compiled using _inp,_outp by VC++
// 2.82:    If success, return 0 
// 2.83:    checksum is printed by 4digit hex value
// 2.91:    '/2 /d:x' bug is fixed.
// 2.92:    read-only file can be transmitted

//#include <dos.h>
#include <stdio.h>
#include <process.h>
#include <conio.h>
#include <stdlib.h>

#define outportb _outp
#define inportb _inp

#define TXR             0       /*  Transmit register (WRITE) */
#define RXR             0       /*  Receive register  (READ)  */
#define IER             1       /*  Interrupt Enable          */
#define IIR             2       /*  Interrupt ID              */
#define LCR             3       /*  Line control              */
#define MCR             4       /*  Modem control             */
#define LSR             5       /*  Line Status               */
#define MSR             6       /*  Modem Status              */
#define DLL             0       /*  Divisor Latch Low         */
#define DLH             1       /*  Divisor latch High        */

#define FIFOCON         2       /*  FIFOCON                   */

void istbe(void);
void sendbyte(unsigned int data);
void transmit(void);
void receive(void);

FILE *stream,*stream2;
unsigned char flename[20];
int comport=1;
unsigned comportbase=0x3f8;
unsigned int gmode=0;
unsigned int fmode=0;
unsigned int rmode=0;
unsigned int divider=3;
unsigned int noinit=0;
char *rxbuf;
int timeoutlimit=100000;

unsigned int rxcount,rxbufsize;
unsigned long filestart,fileend,datalength;

int main(int argc,char *argv[])
{
    int tmp;
    unsigned char temp;
    printf("     +--------------------------------+\n");
    printf("     | BIN FILE TRANSFER USING RS232C |\n");
    printf("     |                   ELECTRANG 91 |\n");
    printf("     |     WKCOM2 VER 2.92            |\n");
    printf("     +--------------------------------+\n");
    printf("8BIT,1 Stop,No Parity,(38.4K)\n");
    printf("Stream Format:< n + 6 >(4)+data(n byte)+checksum(2)\n\n");

    if (argc<3)
    {
        printf("\nUsage:wkcom2 <src_file> /<com#:1,2> [/g] [/f:...] [/d:n] [/n] [/r:buf_size]\n");
	printf("\nOptions\n");
        printf("   /0,/1,/2: output direction. 0:file,1:COM1,2:COM2\n");
	printf("   /g      : no 4byte_head and 2byte_checksum is added.\n");
	printf("   /f:<xx> : output file name\n");
	printf("   /d:n    : baud rate. n=12(9600),6(19200),3(38400),2(57600),1(115200)\n");	                 
        printf("   /n      : No COM port initialization\n");
	printf("   /r:n    : N/A\n");
	printf("   /t:n    : N/A(100000)\n");
	printf("Uasges:Tx to   COM1: wkcom2 tttt.bin /1 /d:1\n");
	printf("       Tx to   file: wkcom2 xxxx.bin /0 /n /f:yyyy.bhc\n");
        exit(0);
    }

    printf("Selected options:\n");
    // select com port
    //comport=*(argv[2]+1)-'0'-1;
    //comportbase=0x3f8;                            //COM1
    //if (comport>=1){comport=1;comportbase=0x2f8;} //COM2

    for(tmp=1;tmp<argc;tmp++)
    {
	if(*(argv[tmp]+0)!='/')continue;
        switch(*(argv[tmp]+1))
        {
	    case '0':
		comport=-1;
		comportbase=0;      //no COM port
                printf("/0: no COM port\n");
		break;

	    case '1':
		comport=0;
		comportbase=0x3f8;  //COM1
                printf("/1: COM1 is selected.\n");
		break;

	    case '2':
		comport=1;
		comportbase=0x2f8;  //COM2
	        printf("/2: COM2 is selected.\n");
		break;

            case 'g':
                printf("/g: No head & CheckSum\n");
                gmode=1;
                break;

            case 'f':
                printf("/f: dest. is file.\n");
                fmode=1;
                stream2 = fopen((argv[tmp]+3),"wb");
		if(stream2==NULL)
		{
		    printf("\nERROR:file creation error.\n");
		    exit(0);
		}
                break;

            case 'd':
                divider=atoi(argv[tmp]+3);
                printf("/d: divider=%d\n",divider);
                break;

            case 'n':
                noinit=1;
                printf("/n: no com port init.\n");
                break;

            case 'r':
                rmode=1;
                rxbufsize = atoi(argv[tmp]+3);
		printf("/r: receive mode buffer=%d\n",rxbufsize);
                break;

            case 't':
                timeoutlimit=atoi(argv[tmp]+3);
                printf("/t: timeoutlimit=%d\n",timeoutlimit);
                break;

        }
    }

    if(rmode==1 && fmode==0)
    {
	printf("\nERROR:/r option has to be used together /f\n");
	return 0;
    }
    
    if(rmode)
    {
	rxbuf=(char *)malloc(rxbufsize);
	if(rxbuf==0x0)
	    printf("\nERROR:memory allocation error.\n");
    }

    if(!rmode)
    {
	stream = fopen(argv[1],"rb");
	if(stream==NULL)
	{
	    printf("\nERROR:can't find the file.\n");
	    exit(0);
	}

	fseek(stream,0L,SEEK_END);
	fileend=ftell(stream);
	fseek(stream,0L,SEEK_SET);
	filestart=ftell(stream);

	datalength=fileend-filestart;  /*fileend == peof+1 */
		    /*data transmition need not to use interrupt*/
    }

    if(comport>=0 && noinit==0)
    {
        //bioscom(0,DATA8+STOPBIT1+NOPARITY+B9600,comport);
        outportb((unsigned short)(comportbase+LCR),0x3);

        temp = inportb((unsigned short)(comportbase+LCR));
        outportb((unsigned short)(comportbase+LCR),(int)temp | 0x80);
        outportb((unsigned short)(comportbase+DLH),(int)(divider/256)); /* 1843200/(divider*16) */
        outportb((unsigned short)(comportbase+DLL),(int)divider%256);
        outportb((unsigned short)(comportbase+LCR),(int)temp);
    }

    if(rmode)
	receive();
    else
	transmit();
    return 0;
}



void transmit(void)
{
    unsigned i;
    unsigned checksum=0,data;
        
    if(!gmode)
    {
       sendbyte((unsigned int)( (datalength +6L) & 0xffL));
              /*transfer D0 byte of datalength*/
       sendbyte((unsigned int)( ((datalength +6L)>>8) & 0xffL));
              /*transfer D1 byte of datalength*/
       sendbyte((unsigned int)( ((datalength +6L)>>16) & 0xffL));
              /*transfer D2 byte of datalength*/
       sendbyte((unsigned int)( ((datalength +6L)>>24) & 0xffL));
              /*transfer D3 byte of datalength*/
    }

    printf("\ndata size:%lu\n",datalength);
    data = fgetc(stream);
    for(i=1;i<=datalength;i++)
    {
         sendbyte(data);
         checksum=checksum+data;
         data = fgetc(stream);
         if(i%1000==0)printf("\rXfered Size=%ld",i);
    }
    printf("\rtransfered Size=%ld\n",i-1L);
    if(!gmode)
    {
         sendbyte(( (unsigned int)checksum ) & 0xff);
             /*transfer lower byte of checksum*/
         sendbyte( (((unsigned int)checksum)>>8) & 0xff );
             /*transfer higherbyte of checksum*/
    }
    if(fmode)fclose(stream2);
    printf("checksum:0x%x\n",checksum & 0xffff);
    
    fclose(stream);
}


void sendbyte(unsigned int data)
{
    if(fmode!=0)
    {
        fputc(data,stream2);
    }
    else
    {
        if(comport>=0)
        {
            istbe();
            outportb((unsigned short)(comportbase+TXR),(int)data);
        }
    }
}


void istbe(void)
{
    //while(    (  ( (unsigned int)bioscom(3,0,comport) ) & 0x2000  )==0   );
    while((inportb((unsigned short)(comportbase+LSR)) & 0x20) == 0);
}


void _sendchar(unsigned int data)
{
            istbe();
            outportb((unsigned short)(comportbase+TXR),(int)data);
}


char _receivechar(void)
{
        while((inportb((unsigned short)(comportbase+LSR)) & 0x1)==0);
	return inportb((unsigned short)(comportbase+RXR));
}


void receive(void)
{
    int timeout=0x0,i;
    char *rxbufpt=rxbuf;    
    int pktsize,invpktsize;
    int piderr=0,timeouterr=0,pktcnt;
    int retrycnt=0;

    rxcount=0;

    inportb((unsigned short)(comportbase+RXR)); //remove garbage

    printf("Ready to receive.\n");
    
    //to signal receive ready state to the transmitter device

    outportb((unsigned short)(comportbase+FIFOCON),(int)3);
    outportb((unsigned short)(comportbase+IER),(int)0);

    _sendchar('S'); 

    while(1)
    {
	pktsize=_receivechar();
	invpktsize=_receivechar();


	if((unsigned char)pktsize!=(unsigned char)(~invpktsize))
	    {piderr=1;pktsize=255;} //packit size is corrupted.

	if(pktsize==0) //the end packit
	{
	    _sendchar('Y');break;
	}

	for(i=0;i<pktsize;i++)
	{
	    while( (inportb((unsigned short)(comportbase+LSR))&0x1)==0)
	    {
		timeout++;
		if(timeout==100000){timeouterr=1;break;}
	    }
	    if(timeouterr!=0)break;
	    timeout=0;
	    *rxbufpt++=inportb((unsigned short)(comportbase+RXR));
	    rxcount++;
	    pktcnt++;
	}

	if(piderr==0 && timeouterr==0 && i==pktsize)
	{
	    _sendchar('Y');
	    retrycnt=0;
	}
	else
	{
	    rxcount-=pktcnt;
	    rxbufpt-=pktcnt;
	    retrycnt++;
	    if(piderr!=0)printf("P");
	    else if(timeouterr!=0)printf("T");
	    _sendchar('R');
	}
	pktcnt=0;
	piderr=0;
	timeouterr=0;
    }

    printf("\nRcv. file size:%lxh\n",rxcount);
    rxbufpt=rxbuf;
    for(i=0;i<rxcount;i++)
	sendbyte(*rxbufpt++);
    printf("File write end.\n");
    free(rxbuf);
}

